﻿// ===================================
// <copyright>LogoUI Co.</copyright>
// <author>Vlad Spivak</author>
// <email>mailto:vlads@logoui.co.il</email>
// <created>21/00/10</created>
// <lastedit>Sunday, November 21, 2010</lastedit>

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//
// <remarks>Part of this software based on various internet sources, mostly on the works
// of members of Wpf Disciples group http://wpfdisciples.wordpress.com/
// Also project may contain code from the frameworks: 
//        Nito 
//        OpenLightGroup
//        nRoute
// </remarks>
// ====================================================================================//


#if SILVERLIGHT

using System;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Browser;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Input;

namespace LogoFX.Infra.Interactivity
{

  /// <summary>
  /// A behavior that can be associated with an element that supports
  /// vertical scrolling.
  /// </summary>
  [Obsolete("Fixed in SL4: native wheel support introduced")]
  public sealed class MouseWheelBehaviour : BehaviorBase<MouseWheelBehaviour>
  {

    private IScrollProvider _scrollProvider;
    private bool _activated;

    /// <summary>
    /// Initializes an instance of a MouseWheelBehaviour.
    /// </summary>
    public MouseWheelBehaviour()
    {
    }

    private ScrollViewer GetChildScrollViewer(DependencyObject parentObject)
    {
      for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parentObject); i++)
      {
        DependencyObject child = VisualTreeHelper.GetChild(parentObject, i);

        ScrollViewer scrollViewer = child as ScrollViewer;
        if (scrollViewer == null)
        {
          scrollViewer = GetChildScrollViewer(child);
        }
        if (scrollViewer != null)
        {
          return scrollViewer;
        }
      }

      return null;
    }

    /// <internalonly />
    protected override void OnAttach()
    {
      base.OnAttach();

      if (HtmlPage.IsEnabled == false)
      {
        return;
      }

      Owner.MouseEnter += OnMouseEnter;
      Owner.MouseLeave += OnMouseLeave;
      Owner.MouseMove += OnMouseMove;
    }

    private void OnBrowserDOMMouseScroll(object sender, HtmlEventArgs e)
    {
      // Handles DOMMouseScroll on Firefox

      double delta = (double)e.EventObject.GetProperty("detail") / -3;
      UpdateScrollOffset(delta);

      e.PreventDefault();
    }

    private void OnBrowserMouseWheel(object sender, HtmlEventArgs e)
    {
      // Handles onmousewheel on non-Firefox browsers

      double delta = (double)e.EventObject.GetProperty("wheelDelta") / 120;
      UpdateScrollOffset(delta);

      e.EventObject.SetProperty("returnValue", false);
    }

    /// <internalonly />
    protected override void OnDetach()
    {
      UpdateDOMEventHandlers(/* activate */ false);

      Owner.MouseEnter -= OnMouseEnter;
      Owner.MouseLeave -= OnMouseLeave;
      Owner.MouseMove -= OnMouseMove;

      base.OnDetach();
    }

    private void OnMouseEnter(object sender, MouseEventArgs e)
    {
      UpdateDOMEventHandlers(/* activate */ true);
    }

    private void OnMouseLeave(object sender, MouseEventArgs e)
    {
      UpdateDOMEventHandlers(/* activate */ false);
    }

    private void OnMouseMove(object sender, MouseEventArgs e)
    {
      UpdateDOMEventHandlers(/* activate */ true);
    }

    private void UpdateDOMEventHandlers(bool activate)
    {
      if (_activated != activate)
      {
        _activated = activate;
        if (activate)
        {
          IScrollProvider scrollProvider = null;

          AutomationPeer automationPeer = FrameworkElementAutomationPeer.CreatePeerForElement(Owner);
          if (automationPeer != null)
          {
            scrollProvider = (IScrollProvider)automationPeer.GetPattern(PatternInterface.Scroll);
          }

          if (scrollProvider == null)
          {
            ScrollViewer scrollViewer = GetChildScrollViewer(Owner);
            if (scrollViewer != null)
            {
              automationPeer = FrameworkElementAutomationPeer.CreatePeerForElement(scrollViewer);
              if (automationPeer != null)
              {
                scrollProvider = (IScrollProvider)automationPeer.GetPattern(PatternInterface.Scroll);
              }
            }
          }

          if ((scrollProvider != null) && scrollProvider.VerticallyScrollable)
          {
            _scrollProvider = scrollProvider;

            HtmlPage.Document.AttachEvent("DOMMouseScroll", OnBrowserDOMMouseScroll);
            HtmlPage.Document.AttachEvent("onmousewheel", OnBrowserMouseWheel);
          }
        }
        else
        {
          if (_scrollProvider != null)
          {
            _scrollProvider = null;

            HtmlPage.Document.DetachEvent("DOMMouseScroll", OnBrowserDOMMouseScroll);
            HtmlPage.Document.DetachEvent("onmousewheel", OnBrowserMouseWheel);
          }
        }
      }
    }

    private void UpdateScrollOffset(double delta)
    {
      ScrollAmount verticalScroll = delta < 0.0 ? ScrollAmount.SmallIncrement : ScrollAmount.SmallDecrement;
      _scrollProvider.Scroll(ScrollAmount.NoAmount, verticalScroll);
    }

    #region Enabled attached property

    public static bool GetEnabled(DependencyObject obj)
    {
      return (bool) obj.GetValue(EnabledProperty);
    }

    public static void SetEnabled(DependencyObject obj, bool value)
    {
      obj.SetValue(EnabledProperty, value);
    }

    public static readonly DependencyProperty EnabledProperty =
      DependencyProperty.RegisterAttached("Enabled", typeof (bool), typeof (MouseWheelBehaviour), new PropertyMetadata(false, OnEnabledChanged));

    private static void OnEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      if((bool)e.NewValue)
      {
        GetOrAttach(d as FrameworkElement);
      }
      else
      {
        Detach(d as FrameworkElement);
      }

    }

    #endregion
  }
}
#endif